﻿#if DEBUG
#define CC_DUMP_FILE
#endif

namespace compound_doc
{
    namespace handle_formula
    {
        using System;
        using System.Xml;
        using System.Collections.Generic;
        using et_Type;

        public class EnumHlp
        {
            public static biff8_ptg GetPtg(byte ptg)
            {
                if (Enum.IsDefined(typeof(biff8_ptg), ptg))
                {
                    return (biff8_ptg)ptg;
                }
                else
                {
                    return biff8_ptg.NONE;
                }
            }

            public static biff8_FunctionID GetFunctionID(ushort uId)
            {
                if (Enum.IsDefined(typeof(biff8_FunctionID), uId))
                {
                    return (biff8_FunctionID)uId;
                }
                else
                {
                    return biff8_FunctionID.NONE;
                }
            }
        }

        public class CellHdr
        {
            public ushort row;
            public ushort col;
            public ushort ixfe;
            public CellHdr()
            {
            }

            public override string ToString()
            {
                return string.Format("Idx: sheet={0}; row={1}; col={0}", ixfe, row, col);
            }
            
            public CellHdr(ushort sheet, ushort r, ushort c)
            {
                ixfe = sheet;
                row = r;
                col = c;
            }
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }

            public bool Equals(CellHdr other)
            {
                if (ReferenceEquals(null, other))
                {
                    return false;
                }
                if (ReferenceEquals(this, other))
                {
                    return true;
                }

                return other.ixfe == ixfe && other.row == row && other.col == col;
            }

            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj))
                {
                    return false;
                }
                if (ReferenceEquals(this, obj))
                {
                    return true;
                }
                if (obj.GetType() != typeof(CellHdr))
                {
                    return false;
                }
                return Equals((CellHdr)obj);
            }

            public static bool operator ==(CellHdr left, CellHdr right)
            {
                return Equals(left, right);
            }

            public static bool operator !=(CellHdr left, CellHdr right)
            {
                return !Equals(left, right);
            }

            public static int Compare(CellHdr left, CellHdr right)
            {
                int nRet = left.ixfe.CompareTo(right.ixfe);
                if (0 != nRet)
                {
                    return nRet;
                }

                nRet = left.row.CompareTo(right.row);
                if (0 != nRet)
                {
                    return nRet;
                }
                return left.col.CompareTo(right.col);
            }
        }

        public class ICellHdrCmp : IComparer<CellHdr>
        {
            public int Compare(CellHdr left, CellHdr right)
            {
                return CellHdr.Compare(left, right);
            }
        }

        public class CellHdrHlp : CellHdr
        {
            // 插入元素，保证不重复且有序
            public static void AddListSorted(List<CellHdr> lst, CellHdr cell)
            {
                // index为负数表示大于item的第一个元素的索引的按位求补
                int index = lst.BinarySearch(cell, new ICellHdrCmp());
                if (index < 0)
                {
                    if ((~index) == lst.Count)
                    {
                        lst.Add(cell);
                    }
                    else
                    {
                        lst.Insert((~index), cell);
                    }
                }
            }
        }

        public class CellRef
        {
            public ushort idx;
            public biff8_ptgRefToken cell;
            public bool Equals(CellRef other)
            {
                if (ReferenceEquals(null, other))
                {
                    return false;
                }
                if (ReferenceEquals(this, other))
                {
                    return true;
                }
                return idx.Equals(other.idx) && cell.Equals(other.cell);
            }

            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj))
                {
                    return false;
                }
                if (ReferenceEquals(this, obj))
                {
                    return true;
                }
                if (obj.GetType() != typeof(CellRef))
                {
                    return false;
                }

                return Equals((CellRef)obj);
            }

            public override int GetHashCode()
            {
                return idx.GetHashCode() ^ cell.GetHashCode();
            }
        }

        public class AreaRef
        {
            public ushort idx;
            public biff8_ptgAreaToken area;

            public bool Equals(AreaRef other)
            {
                if (ReferenceEquals(null, other))
                {
                    return false;
                }
                if (ReferenceEquals(this, other))
                {
                    return true;
                }
                return idx.Equals(other.idx) && area.Equals(other.area);
            }

            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj))
                {
                    return false;
                }
                if (ReferenceEquals(this, obj))
                {
                    return true;
                }
                if (obj.GetType() != typeof(AreaRef))
                {
                    return false;
                }
                
                return Equals((AreaRef)obj);
            }

            public override int GetHashCode()
            {
                return idx.GetHashCode() ^ area.GetHashCode();
            }
        }

        public class biff8_FORMULA : CellHdr
        {
            public byte[] num;
            protected ushort grbit;
            public bool IsShrFmla()
            {
                return (0 != (0x08 & grbit));
            }

            public uint chn;
            public ushort cce;

            // 返回未解析的缓存
            public byte[] Read(byte[] data)
            {
                row = BitConverter.ToUInt16(data, 0);
                col = BitConverter.ToUInt16(data, 2);
                //ixfe = BitConverter.ToUInt16(data, 4);  
                num = new byte[8];
                Array.Copy(data, 6, num, 0, 8);
                grbit = BitConverter.ToUInt16(data, 14);
                chn = BitConverter.ToUInt32(data, 16);
                cce = BitConverter.ToUInt16(data, 20);

                int nSize = data.Length - 22;
                System.Diagnostics.Debug.Assert(nSize >= 0);
                byte[] ret = new byte[nSize];
                Array.Copy(data, 22, ret, 0, nSize);
                return ret;
            }

            public string GetResult()
            {
                string sRet = null;
                // 根据最高两个字节是否为 0xFFFF 识别
                ushort fExprO = BitConverter.ToUInt16(num, 6);
                if (fExprO == 0xFFFF)
                {
                    switch (num[0])
                    {
                        case 0x0:
                            sRet = CFormula.RES_STRING;
                            break;
                        case 0x1:
                            byte b = num[2];
                            sRet = (b != 0) ? CFormula.RES_BOOL_T : CFormula.RES_BOOL_F;
                            break;
                        case 0x2:
                            CPtgHlp.TracePtgError(num[2], out sRet);
                            break;
                        case 0x3:
                            sRet = CFormula.RES_NULL_STR;
                            break;
                        default:
                            sRet = CFormula.RES_UNKNOWN;
                            break;
                    }
                }
                else
                {
                    Double d = BitConverter.ToDouble(num, 0);
                    sRet = d.ToString();
                }

                return sRet;
            }
        }

        public class CRect
        {
            public ushort rwFirst;
            public ushort rwLast;
            public byte colFirst;
            public byte colLast;
        }

        public class biff8_SHRFMLA : CRect
        {
            public ushort reserved;
            public ushort cce;
            public biff8_SHRFMLA(byte[] dat, ref int iPos)
            {
                rwFirst = BitConverter.ToUInt16(dat, 0);
                rwLast = BitConverter.ToUInt16(dat, 2);
                colFirst = dat[4];
                colLast = dat[5];
                cce = BitConverter.ToUInt16(dat, 8);
                iPos += 10;
            }
        }

        public class biff8_ARRAY : CRect
        {
            public ushort grbit;
            public uint chn;
            public ushort cce;
            public biff8_ARRAY(byte[] dat, ref int iPos)
            {
                rwFirst = BitConverter.ToUInt16(dat, 0);
                rwLast = BitConverter.ToUInt16(dat, 2);
                colFirst = dat[4];
                colLast = dat[5];
                cce = BitConverter.ToUInt16(dat, 12);
                iPos += 14;
            }
        }

        public class biff8_NAME
        {
            public ushort grbit;
            public byte chKey;
            public byte cch;
            public ushort cce;
            public ushort ixals;
            public ushort itab;
            public byte cchCustMenu;
            public byte cchDescription;
            public byte cchHelptopic;
            public byte cchStatustext;
            public byte grbitCHR;   // 15 bytes
            public biff8_NAME(byte[] dat, ref int iPos)
            {
                cch = dat[3];
                grbitCHR = dat[14];
                int nNameLen = cch * (grbitCHR + 1);
                iPos += (15 + nNameLen);
            }
        }

        public class biff8_XTI
        {
            public short iSupBook;	// Index (0-based) to table of SUPBOOK redcords
            public short itabFirst;	// Index (0-based) to first sheet tab in the reference
            public short itabLast;	// Index (0-based) to last sheet tab in the reference
            public int Parse(byte[] dat, int iPos)
            {
                iSupBook = BitConverter.ToInt16(dat, iPos);
                itabFirst = BitConverter.ToInt16(dat, iPos + 2);
                itabLast = BitConverter.ToInt16(dat, iPos + 4);
                return iPos + 6;
            }
        };

        public class biff8_EXTERNSHEET
        {
            public ushort cXTI;
            public List<biff8_XTI> rgXTI;
            public biff8_EXTERNSHEET()
            {
                rgXTI = new List<biff8_XTI>();
            }
            public void Parse(byte[] dat)
            {
                cXTI = BitConverter.ToUInt16(dat, 0);
                if( (2 + cXTI * 6) > dat.Length)
                {
                    return;
                }
                int iPos = 2;
                for(int i = 0; i < cXTI; ++i)
                {
                    biff8_XTI xti = new biff8_XTI();
                    iPos = xti.Parse(dat, iPos);
                    rgXTI.Add(xti);
                }
            }
        }

        public class CExternSheet
        {
            private biff8_EXTERNSHEET m_biff8ES;
            public CExternSheet()
            {
                m_biff8ES = new biff8_EXTERNSHEET();
            }
            public void Parse(byte[] dat)
            {
                m_biff8ES.Parse(dat);
            }
            public List<bool> GetItems()
            {
                List<bool> extbook = new List<bool>();
                foreach (biff8_XTI xti in m_biff8ES.rgXTI)
                {
                    extbook.Add(xti.iSupBook != 0);
                }

                return extbook;
            }
        }

        public class biff8_EXTERNNAME
        {
            public ushort grbit;
            public uint reserved;
            public byte cch;
            public byte grbitCHR;
            public biff8_EXTERNNAME(byte[] dat, ref int iPos)
            {
                grbit = BitConverter.ToUInt16(dat, 0);
                reserved = BitConverter.ToUInt32(dat, 2);
                cch = dat[6];
                grbitCHR = dat[7];
                iPos += 8;
            }
        }

        public class biff8_BOUNDSHEET
        {
            //private uint lbPlyPos;
            //private byte hsState;
            //private byte dt;
            public byte cch;
            public byte grbitCHR;
            private string m_sheetName;
            public biff8_BOUNDSHEET(byte[] dat)
            {
                cch = dat[6];
                grbitCHR = dat[7];
                int iPos = 8;
                if(cch > dat.Length)
                {
                    throw new tool.ETImpException(tool.LogTool.LOG_TYPE.FILE_ERROR, null,
                        string.Format("error: BOUNDSHEET record: cch={0}, buf length={1}", cch, dat.Length));
                }
                CBiffHlp.DecodeString(dat, ref iPos, cch, grbitCHR, out m_sheetName);
            }

            public string GetSheetName()
            {
                return m_sheetName;
            }
        }

        public enum FMLA_TYPE
        {
            FT_NAME,
            FT_EXTERN_NAME,
            FT_NORMAL,
            FT_ARRAY,
            FT_SHARE,
            FT_UNKNOWN
        }

        public class CFmlaNameItem
        {
            public int Index { set; get; }
            public int Itxi { set; get; }   //  itxi = -1 就是Name，否则可能是ExternName
            public bool bExternName;
            public CFmlaNameItem(int _idx)
            {
                Index = _idx;
                Itxi = -1;
            }

            public CFmlaNameItem(int _idx, int _ixti)
            {
                Index = _idx;
                Itxi = _ixti;
            }

            public override string ToString()
            {
                return string.Format(" index:{0} itxi:{1} bExternName:{2}", Index, Itxi, bExternName);
            }
        }

        public class CFormula
        {
            public FMLA_TYPE fmType;    // cell指向的公式类型
            public bool m_bExpVolatile; // 显示易失函数 rand etc
            public bool m_bImpVolatile; // 隐式易失函数 offset etc
            public int m_nFmlaIdx;  // shrFmla/ArrayFmla 表示在本sheet中List的下标
            public List<int> m_nameIndex;
            public bool m_bExternName;
            public string sResult;
            public CellHdr m_cell;
            public CellHdr m_firstCell;
            public HashSet<CellRef> m_cellRef; // 引用的单元格
            public HashSet<AreaRef> m_areaRef;
            // 结果值
            public static readonly string RES_STRING = "A_STRING";
            public static readonly string RES_BOOL_T = "TRUE";
            public static readonly string RES_BOOL_F = "FALSE";
            public static readonly string RES_NULL_STR = "NULL";
            public static readonly string RES_UNKNOWN = "UNKNOWN";

            public CFormula()
            {
                m_nFmlaIdx = -1;
                m_cell = new CellHdr();
                m_firstCell = new CellHdr();
                m_bExternName = false;
            }

            // 解析Formula
            public void Prase(byte[] data, int sheetIdx)
            {
                biff8_FORMULA fmla = new biff8_FORMULA();
                byte[] remain = fmla.Read(data);
                if(22 + fmla.cce > data.Length)
                {
                    return;
                }

                m_cell.ixfe = (ushort)sheetIdx;
                m_cell.row = fmla.row;
                m_cell.col = fmla.col;
                sResult = fmla.GetResult();

                DecodeFormula(FMLA_TYPE.FT_UNKNOWN, remain, fmla.cce, sheetIdx, m_cell,
                     fmla.IsShrFmla(), false);
            }

            // bShrFmla只在解析FORMULA记录才为true 根据biff8_FORMULA来的
            public void DecodeFormula(FMLA_TYPE preFmType, byte[] dat, int fmSize, int sheetidx,
                                    CellHdr curCell, bool bShrFmla, bool bRelOffset)
            {
                FormulaDecoder fd = new FormulaDecoder(preFmType, curCell);
                fd.Decode(dat, fmSize, m_cell.ixfe, bRelOffset);
                m_areaRef = fd.GetAreaRef();
                m_cellRef = fd.GetCellRef();
                m_bExpVolatile = fd.IsExpVol();
                m_bImpVolatile = fd.IsImpVol();
				m_firstCell = fd.GetFirstCell();
                m_nameIndex = fd.GetNameIndex(ref m_bExternName);
                fmType = FormulaDecoder.GetPointFmlaType(m_bExternName, m_nameIndex, m_firstCell, bShrFmla);
            }
#if ENABLE_ET_COM
#if CC_DUMP_FILE
            public void DumpOutput()
            {
                string sInfo = suite_compare_cell.CDataMgr.GetCellString(m_cell.ixfe, m_cell.row, m_cell.col);
                sInfo += string.Format(" TYPE:{0} RESULT:{1}", fmType.ToString(), sResult);
                if(fmType == FMLA_TYPE.FT_NAME)
                {
                    foreach (int idx in m_nameIndex)
                    {
                        sInfo += string.Format(" Name{0}", idx);
                    }
                }
                sInfo += string.Format(" bExp:{0} bImp:{1}", m_bExpVolatile, m_bImpVolatile);
                tool.Output.GetInstance().DumpLine(sInfo);
            }
#endif
#endif
        }

        public class CShrFmla
        {
            public CellHdr m_firstCell;
            public CFormula m_fmla;     // 真正的公式 存的是相对引用
            public void Prase(byte[] dat, int sheetIdx)
            {
                int iPos = 0;
                biff8_SHRFMLA sf = new biff8_SHRFMLA(dat, ref iPos);
                byte[] remain = new byte[dat.Length - iPos];
                Array.Copy(dat, iPos, remain, 0, dat.Length - iPos);

                m_fmla = new CFormula();
                m_fmla.DecodeFormula(FMLA_TYPE.FT_SHARE, remain, sf.cce, sheetIdx,
                    null, false, true); //  bShrFmla应为false
            }
        }

        // array formula 的rect区域全是arrayFormula
        public class CArrayFmla
        {
            public CellHdr m_firstCell;
            public CFormula m_fmla;         //  公式存的是绝对引用
            public void Prase(byte[] dat, int sheetIdx)
            {
                int iPos = 0;
                biff8_ARRAY arr = new biff8_ARRAY(dat, ref iPos);

                byte[] remain = new byte[dat.Length - iPos];
                Array.Copy(dat, iPos, remain, 0, dat.Length - iPos);

                m_fmla = new CFormula();
                m_fmla.DecodeFormula(FMLA_TYPE.FT_ARRAY, remain, arr.cce, sheetIdx,
                    null, false, false);
            }
        }

        public class CName
        {
            public CFormula m_fmla;
            public void Prase(byte[] data)
            {
                int iPos = 0;
                biff8_NAME name = new biff8_NAME(data, ref iPos);
                byte[] remain = new byte[data.Length - iPos];
                Array.Copy(data, iPos, remain, 0, data.Length - iPos);

                m_fmla = new CFormula();
                m_fmla.DecodeFormula(FMLA_TYPE.FT_NAME, remain, -1, -1,
                    null, false, true);
            }
        }

        public class CExternName
        {
            public bool bRandBetween;
            public void Prase(byte[] dat)
            {
                int iPos = 0;
                biff8_EXTERNNAME en = new biff8_EXTERNNAME(dat, ref iPos);
                string sName = null;
                bRandBetween = false;
                CBiffHlp.DecodeString(dat, ref iPos, en.cch, en.grbitCHR, out sName);
                if (sName == "RANDBETWEEN")
                {
                    bRandBetween = true;
                }
            }
        }

        public class FormulaDecoder
        {
            protected HashSet<CellRef> m_cellRef;
            protected HashSet<AreaRef> m_areaRef;
            protected bool m_bExpVol;
            protected bool m_bImpVol;
            protected ushort m_sheetIdx;
            protected CellHdr m_fmCell;
            protected CellHdr m_firstCell;  // shrfmla/arrayfmla专用
            protected List<int> m_nameIdx;
            protected bool m_bExternName;
            
            private bool m_bRelativeAsOffset;
            private FMLA_TYPE m_fmType;     //  当前正在处理的字段类型，而不是公式指向的类型 

            public FormulaDecoder(FMLA_TYPE curFmType, CellHdr cell)
            {
                m_bExpVol = false;
                m_bImpVol = false;
                m_firstCell = null;
                m_nameIdx = new List<int>();
                m_fmCell = cell;
                m_fmType = curFmType;
            }

            public List<int> GetNameIndex(ref bool  bExternName)
            {
                bExternName = m_bExternName;
                return m_nameIdx;
            }

            // cell指向的公式类型
            public static FMLA_TYPE GetPointFmlaType(bool bExternName, List<int> nameIndex, CellHdr firstCell, bool bShrFmla)
            {
                if(bExternName)
                {
                    return FMLA_TYPE.FT_EXTERN_NAME;
                }

                if (nameIndex != null && nameIndex.Count > 0)
                {
                    return FMLA_TYPE.FT_NAME;
                }

                if (firstCell == null)
                {
                    return FMLA_TYPE.FT_NORMAL;
                }

                // 判断是ShrFmla还是ArrayFmla
                if (bShrFmla)
                {
                    return FMLA_TYPE.FT_SHARE;
                }
                else
                {
                    return FMLA_TYPE.FT_ARRAY;
                }
            }

            // bRelativeAsOffset标记表明row/col相对位置要存一个偏移值，而不是存绝对位置值，
            public bool Decode(byte[] extDat, int nSize, int sheetIdx, bool bRelativeAsOffset)
            {
                m_bRelativeAsOffset = bRelativeAsOffset;
                m_sheetIdx = (ushort)sheetIdx;
                int iPos = 0;
                int nLength = nSize;
                if(nSize < 0)
                {
                    nLength = extDat.Length;
                }
                byte ptg;
                while (iPos < nLength)
                {
                    ptg = extDat[iPos++];
                    if (ptg >= 0x80)
                    {
                        // unknown ptg
                        break;
                    }
#if DUMP_PTG
                    int n1 = iPos;
#endif
                    iPos = DecodePtgExcel(extDat, iPos, ptg);
#if DUMP_PTG
                    int nSize = iPos - n1;
                    tool.Output.GetInstance().Dump("ptg=" + ptg.ToString("X") + "\tsize=" + nSize.ToString("X"));
#endif
                }

                return true;
            }

            public HashSet<CellRef> GetCellRef()
            {
                return m_cellRef;
            }

            public HashSet<AreaRef> GetAreaRef()
            {
                return m_areaRef;
            }

            public CellHdr GetFirstCell()
            {
                return m_firstCell;
            }

            public bool IsExpVol()
            {
                return m_bExpVol;
            }

            public bool IsImpVol()
            {
                return m_bImpVol;
            }

            protected void CheckFuncID(biff8_FunctionID fID)
            {
                switch (fID)
                {
                    case biff8_FunctionID.biff8_func_RAND:
                    case biff8_FunctionID.biff8_func_TODAY:
                    case biff8_FunctionID.biff8_func_NOW:  // randbetween在NAME中处理
                        m_bExpVol = true;
                        break;
                    case biff8_FunctionID.biff8_func_OFFSET:
                    case biff8_FunctionID.biff8_func_INDIRECT:  //A1:A2:D5这种运算也要考虑
                    case biff8_FunctionID.biff8_func_SUMIF:
                    case biff8_FunctionID.biff8_func_CELL:
                    case biff8_FunctionID.biff8_func_EVALUATE:
                        m_bImpVol = true;
                        break;
                }
            }

            protected int DecodePtgExcel(byte[] dat, int iPos, byte ptg)
            {
                if (ptg >= 0x60) // A
                {
                    ptg -= 0x40;
                }
                else if (ptg >= 0x40) // V
                {
                    ptg -= 0x20;
                }
                //else if(ptg >= 0x20) // R

                string str;
                switch (EnumHlp.GetPtg(ptg))
                {
                    case biff8_ptg.biff8_ptgFuncVar:
                        biff8_ptgFuncVarToken fvt = new biff8_ptgFuncVarToken(dat, ref iPos);
                        CheckFuncID(EnumHlp.GetFunctionID(fvt.GetIftab()));
                        break;
                    case biff8_ptg.biff8_ptgFunc:
                        biff8_ptgFuncToken ft = new biff8_ptgFuncToken(dat, ref iPos);
                        CheckFuncID(EnumHlp.GetFunctionID(ft.iftab));
                        break;
                    case biff8_ptg.biff8_ptgRef3d:
                    case biff8_ptg.biff8_ptgRefErr3d:
                        biff8_ptgRef3dToken r3t = new biff8_ptgRef3dToken(dat, ref iPos);
                        AddToCellRefList(r3t.ixti, r3t.refToken);
                        break;
                    case biff8_ptg.biff8_ptgRefN:
                    case biff8_ptg.biff8_ptgRef:
                        biff8_ptgRefToken rt = new biff8_ptgRefToken(dat, ref iPos);
                        AddToCellRefList(m_sheetIdx, rt);
                        break;
                    case biff8_ptg.biff8_ptgArea:
                        biff8_ptgAreaToken at = new biff8_ptgAreaToken(dat, ref iPos);
                        AddToAreaRefList((short)m_sheetIdx, (short)m_sheetIdx, at);
                        break;
                    case biff8_ptg.biff8_ptgAreaN:
                        biff8_ptgAreaN an = new biff8_ptgAreaN(dat, ref iPos);
                        AddToAreaRefList((short)m_sheetIdx, (short)m_sheetIdx, an.ToAreaToken());
                        break;
                    case biff8_ptg.biff8_ptgArea3d:
                    case biff8_ptg.biff8_ptgAreaErr3d:
                        biff8_ptgArea3dToken a3t = new biff8_ptgArea3dToken(dat, ref iPos);
                        AddToAreaRefList(a3t.sheet, a3t.sheet, a3t.areaToken);
                        break;
                    case biff8_ptg.biff8_ptgExp:
                        biff8_ptgExpToken et = new biff8_ptgExpToken(dat, ref iPos);
                        m_firstCell = new CellHdr();
                        m_firstCell.ixfe = m_sheetIdx;
                        m_firstCell.row = et.rwFrist;
                        m_firstCell.col = et.colFirst;
                        break;
                    case biff8_ptg.biff8_ptgName:   // Name
                        biff8_ptgNameToken nt = new biff8_ptgNameToken(dat, ref iPos);
                        m_nameIdx.Add(nt.ilbl - 1);
                        break;
                    case biff8_ptg.biff8_ptgNameX:  // ExternName
                        biff8_ptgNameXToken nxt = new biff8_ptgNameXToken(dat, ref iPos);
                        m_bExternName = true;
                        break;
                    case biff8_ptg.biff8_ptgStr:
                        biff8_ptgStrToken st = new biff8_ptgStrToken(dat, ref iPos);
                        CBiffHlp.DecodeString(dat, ref iPos, st.cch, st.grbitCHR, out str);
                        break;
                    case biff8_ptg.biff8_ptgRange:
                    case biff8_ptg.biff8_ptgUnion:
                        iPos++;
                        m_bImpVol = true;
                        break;
                    case biff8_ptg.biff8_ptgTbl:
                        iPos = dat.Length;
                        break;
                    case biff8_ptg.biff8_ptgArray:
                        biff8_ptgArrayToken arr = new biff8_ptgArrayToken(dat, ref iPos);
                        iPos += (arr.cRows + 1) * 2 + arr.cClos + 1;
                        break;
                    case biff8_ptg.biff8_ptgAttr:
                        biff8_ptgAttrToken attrT = new biff8_ptgAttrToken();
                        attrT.Decode(dat, ref iPos);
                        break;
                    default:    //  不关心的ptg以及无法识别的ptg
                        if ((ptg < CPtgHlp.biff8_PtgInfos.Length) &&
                            (CPtgHlp.biff8_PtgInfos[ptg].ptgSize >= 0))
                        {
                            iPos += CPtgHlp.biff8_PtgInfos[ptg].ptgSize;
                        }
                        else
                        {
                            iPos = dat.Length;  //  退出
                        }
                        break;
                }

                return iPos;
            }

            protected void AddToCellRefList(ushort sheet, biff8_ptgRefToken rt)
            {
                if (m_cellRef == null)
                {
                    m_cellRef = new HashSet<CellRef>();
                }
                CellRef cell = new CellRef();
                cell.idx = sheet;
                cell.cell = (biff8_ptgRefToken)rt.Clone();

                // 如果名称还有相对引用就把它归为offset一类
                if (m_fmType == FMLA_TYPE.FT_NAME &&
                    (rt.grbitCol.IsRowRelative() || rt.grbitCol.IsColRelative()))
                {
                    m_bImpVol = true;
                }

                m_cellRef.Add(cell);
            }

            protected void AddToAreaRefList(short sheetFrom, short sheetTo,
                biff8_ptgAreaToken areaToken)
            {
                if (m_areaRef == null)
                {
                    m_areaRef = new HashSet<AreaRef>();
                }
                for (short i = sheetFrom; i <= sheetTo; ++i)
                {
                    AreaRef area = new AreaRef();
                    area.idx = (ushort)i;
                    area.area = (biff8_ptgAreaToken)areaToken.Clone();
                    if (m_fmType == FMLA_TYPE.FT_NAME &&
                        (areaToken.grbitColFirst.IsRowRelative() ||
                        areaToken.grbitColTo.IsColRelative()))
                    {
                        m_bImpVol = true;
                    }
                    m_areaRef.Add(area);
                }
            }
        }
    }
}
